package it.dornauf.java.web2printer;

/**
$Id: $ 
Copyright [2001, 2013] [Oliver Dornauf, pda-systems.COM]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.methods.GetMethod;

/**
 * <p>Implementation of <strong>Web2Printer</strong> as a servlet.</p>  
 *
 *
 * @author <a href="mailto:info@pda-systems.com>Oliver Dornauf</a>
 */
public class Web2PrinterServlet extends HttpServlet
{
    private static final long serialVersionUID = 3058441646260741146L;
    private Web2Printer web2printer;
    private HttpClient client;
    private int connectionTimeout;
    private boolean localePerRequest = false;
    private Locale defaultLocale;
    private URL site;
    private Pattern pageParameter;
    /** page parameter maximum length (default is 255) */
    private int maxPathLength;

    /**
     * Constructor of the object.
     */
    public Web2PrinterServlet() {
        super();
    }

    /**
     * Initialization of the servlet. <br>
     * 
     * @throws ServletException
     *             if an error occurs
     */
    @SuppressWarnings("unchecked")
    public void init() throws ServletException {
        // Get the value of an initialisation parameter
        String siteUrl = getServletConfig().getInitParameter("site-url");
        if (siteUrl != null) {
            URI uri = URI.create(siteUrl);
            try {
                site = uri.toURL();
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
        }
        try {
            connectionTimeout = Integer.parseInt(getServletConfig().getInitParameter("timeout"));
        } catch (NumberFormatException ex) {
            connectionTimeout = 1000; // 1 second
        }

        String defaultLocaleName = getServletConfig().getInitParameter("defaultLocale");
        if (defaultLocaleName == null) {
            defaultLocale = Locale.ENGLISH;
        } else {
            defaultLocale = new Locale(defaultLocaleName);
        }
        localePerRequest = new Boolean(getServletConfig().getInitParameter("localePerRequest")).booleanValue();

        String maxPathLengthParameter = getServletConfig().getInitParameter("maxPathLength");
        if (maxPathLengthParameter != null) {
            maxPathLength = Integer.parseInt(maxPathLengthParameter);
        } else {
            maxPathLength = 255;
        }

        client = new HttpClient(new MultiThreadedHttpConnectionManager());
        client.getHttpConnectionManager().getParams().setConnectionTimeout(connectionTimeout);
        String startToken = getServletConfig().getInitParameter("startToken");
        String endToken = getServletConfig().getInitParameter("endToken");

        if (startToken != null && endToken != null) {
            web2printer = new Web2Printer(startToken, endToken);
        } else {
            web2printer = new Web2Printer();
        }
        pageParameter = Pattern.compile("(/[\\w- ./?%&=$]*)");
    }

    /**
     * Destruction of the servlet. <br>
     */
    public void destroy() {
        super.destroy(); // Just puts "destroy" string in log
    }

    /**
     * The doGet method of the servlet. <br>
     * This method is called when a form has its tag value method equals to get.
     * 
     * @param request
     *            the request send by the client to the server
     * @param response
     *            the response send by the server to the client
     * @throws ServletException
     *             if an error occurred
     * @throws IOException
     *             if an error occurred
     */
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        preparePage(request, response);
    }

    /**
     * The doPost method of the servlet. <br>
     * This method is called when a form has its tag value method equals to post.
     * 
     * @param request
     *            the request send by the client to the server
     * @param response
     *            the response send by the server to the client
     * @throws ServletException
     *             if an error occurred
     * @throws IOException
     *             if an error occurred
     */
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        preparePage(request, response);
    }

    private void preparePage(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
            throws IOException {

        String pageUrl = parsePageParameter(httpServletRequest.getParameter("page"));

        if (pageUrl == null) {
            httpServletResponse.sendError(404);
        } else {
            GetMethod get = new GetMethod(pageUrl);
            get.setFollowRedirects(true);

            int result = client.executeMethod(get);
            if (result == 200) {
                StringBuffer content = readContent(new BufferedReader(new InputStreamReader(get.getResponseBodyAsStream(), get
                        .getResponseCharSet())));
                long startTimeMillis = System.currentTimeMillis();

                Vector<String> hyperlinks = new Vector<String>();
                Vector<String> images = new Vector<String>();

                Web2Printer.ImageProcessing img = web2printer.parseImageParameter(httpServletRequest.getParameter("img"));
                Web2Printer.LinkProcessing lnk = web2printer.parseLinkProcessingParameter(httpServletRequest
                        .getParameter("lnk"));
                // Web2Printer.TagProcessing tgs = web2printer.parseTagProcessingParameter(request.getParameter("tgs"));

                StringBuffer htmlBody = web2printer.getHtmlBody(content);
                StringBuffer htmlHeader = web2printer.getHtmlHeader(content);
                StringBuffer htmlFooter = web2printer.getHtmlFooter(content);

                Locale currentLocale = null;
                if (localePerRequest) {
                    currentLocale = httpServletRequest.getLocale();
                } else {
                    currentLocale = defaultLocale;
                }
                ResourceBundle bundle = null;
                try {
                    bundle = ResourceBundle.getBundle("it.dornauf.java.web2printer.messages", currentLocale);
                } catch (MissingResourceException ex) {
                    bundle = ResourceBundle.getBundle("it.dornauf.java.web2printer.messages");
                }

                URL requestURL = new URL(httpServletRequest.getRequestURL().toString());
                if (lnk != Web2Printer.LinkProcessing.NOTHING && lnk != Web2Printer.LinkProcessing.FRAUD) {
                    String protocol = httpServletRequest.getProtocol();
                    String host = requestURL.getHost();
                    int port = requestURL.getPort();
                    htmlBody = web2printer.processLinks(htmlBody, lnk, protocol, host, port, hyperlinks);
                }
                if (img != Web2Printer.ImageProcessing.NOTHING && img != Web2Printer.ImageProcessing.FRAUD) {
                    htmlBody = web2printer.processImages(htmlBody, img, images, bundle);
                }
                Long runtimeTimeMillis = new Long(System.currentTimeMillis() - startTimeMillis);

                httpServletResponse.setContentType("text/html");
                PrintWriter out = httpServletResponse.getWriter();

                String[] authorAndCopyright = web2printer.getAuthor(htmlHeader);
                htmlBody.append(web2printer.createFooter(runtimeTimeMillis, pageUrl, hyperlinks, images,
                        authorAndCopyright[0], authorAndCopyright[1], bundle));
                int length = /* htmlHeader.toString().length() + */htmlBody.toString().length()
                        + htmlFooter.toString().length();
                httpServletResponse.setContentLength(length);
                // out.write(htmlHeader.toString());
                out.write(htmlBody.toString());
                out.write(htmlFooter.toString());
                out.close();
            } else {
                httpServletResponse.sendError(result);
            }
        }
    }

    private StringBuffer readContent(BufferedReader bufferedReader) {
        StringBuffer result = new StringBuffer();
        char[] buffer = new char[4 * 1024];
        int charsRead;
        try {
            while ((charsRead = bufferedReader.read(buffer)) != -1) {
                result.append(buffer, 0, charsRead);
            }
        } catch (IOException e) {
        }
        return result;
    }

    private String parsePageParameter(String parameter) {
        String target = null;

        if (parameter == null || parameter.length() > maxPathLength) {
            return null;
        }
        Matcher validPathParameter = pageParameter.matcher(parameter);
        if (validPathParameter.matches()) {
            try {
                URI uri = new URI(site.getProtocol(), null, site.getHost(), site.getPort(), parameter, null, null);
                target = uri.toString();
            } catch (URISyntaxException e) {
            }
        }
        return target;
    }

}
